#ifndef XG_REDISCONNECT_H
#define XG_REDISCONNECT_H
////////////////////////////////////////////////////////
#include "DBConnect.h"

#ifndef XG_REDIS_BUFFER_MAXSIZE
#define XG_REDIS_BUFFER_MAXSIZE		1024 * 1024
#endif

class RedisConnect : public Object
{
	friend class Command;

public:
	class Command
	{
		friend RedisConnect;

	protected:
		int status;
		string msg;
		vector<string> res;
		vector<string> vec;

	protected:
		int parse(const char* msg, int len);
		const char* parseNode(const char* msg, int len);

	public:
		string toString() const;
		int getResult(RedisConnect* redis, int timeout);

	public:
		Command()
		{
			this->status = 0;
		}
		Command(const string& cmd)
		{
			vec.push_back(cmd);
			this->status = 0;
		}

		string get(int idx) const
		{
			return res.at(idx);
		}
		void add(const char* val)
		{
			vec.push_back(val);
		}
		void add(const string& val)
		{
			vec.push_back(val);
		}
		const vector<string>& getDataList() const
		{
			return res;
		}
		template<class DATA_TYPE> void add(DATA_TYPE val)
		{
			add(stdx::str(val));
		}
		template<class DATA_TYPE, class ...ARGS> void add(DATA_TYPE val, ARGS ...args)
		{
			add(val);
			add(args...);
		}
	};

protected:
	string msg;
	string host;
	Socket sock;
	string password;
	int code = 0;
	int port = 0;
	int memsz = 0;
	int status = 0;
	int timeout = 0;
	SmartBuffer buffer;

	virtual sp<RedisConnect> grasp() const;

public:
	int getPort() const
	{
		return port;
	}
	int getStatus() const
	{
		return status;
	}
	int getErrorCode() const
	{
		if (sock.isClosed()) return XG_ERROR;

		return code < 0 ? code : 0;
	}
	SmartBuffer getBuffer() const
	{
		return buffer;
	}
	const string& getHost() const
	{
		return host;
	}
	const string& getPassword() const
	{
		return password;
	}
	const string& getErrorString() const
	{
		return msg;
	}

public:
	bool reconnect()
	{
		if (host.empty()) return false;

		return connect(host.c_str(), port, timeout, memsz) && auth(password) > 0;
	}
	int execute(Command& cmd)
	{
		return cmd.getResult(this, timeout);
	}
	template<class DATA_TYPE, class ...ARGS>
	int execute(DATA_TYPE val, ARGS ...args)
	{
		Command cmd;

		cmd.add(val, args...);

		return cmd.getResult(this, timeout);
	}
	template<class DATA_TYPE, class ...ARGS>
	int execute(vector<string>& vec, DATA_TYPE val, ARGS ...args)
	{
		Command cmd;

		cmd.add(val, args...);

		cmd.getResult(this, timeout);

		if (code > 0) std::swap(vec, cmd.res);

		return code;
	}
	bool connect(const string& host, int port, int timeout = SOCKET_CONNECT_TIMEOUT, int memsz = XG_REDIS_BUFFER_MAXSIZE)
	{
		CHECK_FALSE_RETURN(sock.connect(host, port, timeout));

		this->host = host;
		this->port = port;
		this->memsz = memsz;
		this->timeout = timeout;

		return buffer.malloc(memsz) ? true : false;
	}

public:
	int ping()
	{
		return execute("ping");
	}
	int del(const string& key)
	{
		return execute("del", key);
	}
	int ttl(const string& key)
	{
		return execute("ttl", key) == XG_OK ? status : code;
	}
	int hlen(const string& key)
	{
		return execute("hlen", key) == XG_OK ? status : code;
	}
	string get(const string& key)
	{
		string res;

		get(key, res);

		return std::move(res);
	}
	int decr(const string& key, int val = 1)
	{
		return execute("decrby", key, val);
	}
	int expire(const string& key, int timeout)
	{
		return execute("expire", key, timeout);
	}
	int keys(vector<string>& vec, const string& key)
	{
		return execute(vec, "keys", key);
	}
	int hdel(const string& key, const string& filed)
	{
		return execute("hdel", key, filed);
	}
	int set(const string& key, const string& val, int timeout = 0)
	{
		return timeout > 0 ? execute("setex", key, timeout, val) : execute("set", key, val);
	}
	int hset(const string& key, const string& filed, const string& val)
	{
		return execute("hset", key, filed, val);
	}

public:
	int pop(const string& key, string& val)
	{
		return lpop(key, val);
	}
	int push(const string& key, const string& val)
	{
		return rpush(key, val);
	}
	int lpush(const string& key, const string& val)
	{
		return execute("lpush", key, val);
	}
	int rpush(const string& key, const string& val)
	{
		return execute("rpush", key, val);
	}
	int range(vector<string>& vec, const string& key, int start, int end)
	{
		return execute(vec, "lrange", key, start, end);
	}
	int lrange(vector<string>& vec, const string& key, int start, int end)
	{
		return execute(vec, "lrange", key, start, end);
	}

public:
	int zrem(const string& key, const string& filed)
	{
		return execute("zrem", key, filed);
	}
	int zadd(const string& key, const string& filed, int score)
	{
		return execute("zadd", key, score, filed);
	}
	int zrange(vector<string>& vec, const string& key, int start, int end, bool withscore = false)
	{
		return withscore ? execute(vec, "zrange", key, start, end, "withscores") : execute(vec, "zrange", key, start, end);
	}

public:
	template<class ...ARGS>
	int eval(const string& lua)
	{
		vector<string> vec;

		return eval(lua, vec);
	}
	template<class ...ARGS>
	int eval(const string& lua, const string& key, ARGS ...args)
	{
		vector<string> vec;
	
		vec.push_back(key);
	
		return eval(lua, vec, args...);
	}
	template<class ...ARGS>
	int eval(const string& lua, const vector<string>& keys, ARGS ...args)
	{
		vector<string> vec;

		return eval(vec, lua, keys, args...);
	}
	template<class ...ARGS>
	int eval(vector<string>& vec, const string& lua, const vector<string>& keys, ARGS ...args)
	{
		int len = 0;
		Command cmd("eval");

		cmd.add(lua);
		cmd.add(len = keys.size());

		if (len-- > 0)
		{
			for (int i = 0; i < len; i++) cmd.add(keys[i]);

			cmd.add(keys.back(), args...);
		}

		cmd.getResult(this, timeout);
	
		if (code > 0) std::swap(vec, cmd.res);

		return code;
	}

public:
	string getLockId();
	bool unlock(const string& key);
	int auth(const string& password);
	int get(const string& key, string& val);
	int lpop(const string& key, string& val);
	int rpop(const string& key, string& val);
	bool lock(const string& key, int timeout = 30);
	string hget(const string& key, const string& filed);
	int incr(const string& key, int val = 1, int timeout = 0);
	int hgetall(const string& key, map<string, string>& resmap);
	int hget(const string& key, const string& filed, string& val);

public:
	static bool CanUse();
	static sp<RedisConnect> Instance();
	static void Setup(const string& host, int port, const string& password = "", int timeout = SOCKET_CONNECT_TIMEOUT, int memsz = XG_REDIS_BUFFER_MAXSIZE);
};

class RedisSession : public Session
{
private:
	sp<RedisConnect> redis;
	mutable map<string, string> datamap;

public:
	void close();
	bool clear();
	bool update();
	bool disable();
	int size() const;
	long getTimeout() const;
	bool setTimeout(long second);
	bool remove(const string& key);
	bool init(const string& name, int timeout);
	bool set(const map<string, string>& attrmap);
	bool set(const string& key, const string& val);
	bool get(const string& key, string& val) const;

	int getErrorCode() const
	{
		return redis ? redis->getErrorCode() : XG_NETERR;
	}
	string getErrorString() const
	{
		return redis ? redis->getErrorString() : "connect error";
	}
	string get(const string& key) const
	{
		return Session::get(key);
	}
};

////////////////////////////////////////////////////////
#endif